This notebook explains the Potential Outcomes Framework and estimation of Causal Effects, in particular the Average Treatment Effect and related estimators: the Intent-to-Treat Effect and the Complier Average Causal Effect (also known as the Local Average Treatment Effect).

Using a generated population as a reference, we examine the implications of random sampling and random assignment of treatment, non-compliance, and consequences of limited sample sizes.

The code needed to run this analysis can be find in the following GitHub repository where you can download, comment, or make contributions of your own.

Requirements

First, you will need RStudio to run this notebook. Please download it here and then install it.

There are two libraries needed to run this R Notebook: 1. tidyverse (for data manipulation) 2. randomNames (to generate the population)

You may experience errors trying to install these packages. Your best friend, in this case, is Google. Search for the error messages you receive and try to troubleshoot your way through installing the package. Unfortunately there isn’t a consistent solution to these problems as different operating systems and local settings can cause a variety of issues. However, all these issues should be able to be resolved.

Generating up our population

Lets define and generate the population we’re going to study. Below we set some general characteristics: - the size of the population, - gender and ethnicity demographics and - the base level income and variation (standard deviation) of that income.

population_size <- 1000000
base_income <- 10000
base_income_sd <- 1000

ethnicities <- factor(c("Indian", "Asian", "Black", "Hispanic", "White", "Arabic"))
genders <- factor(c("Female", "Male"))

How many people have we defined to be in our population?

What is their expected base income?

How many different ethnicities are in our population?

demographics <- tibble(
  gender = sample(genders, population_size, prob = runif(length(genders)),
                  replace = TRUE),
  ethnicity = sample(ethnicities, population_size, prob = runif(length(ethnicities)),
                     replace = TRUE))

income_male_factor <- runif(1)
income_ethnicity_factor <- runif(1)

population <- randomNames(population_size, return.complete.data = TRUE,
                          gender = as.numeric(demographics$gender) - 1,
                          ethnicity = as.numeric(demographics$ethnicity)) %>%
  mutate(id = factor(row_number())) %>%
  mutate(across(where(is.character), as.factor)) %>%
  mutate(income = round(rnorm(population_size, base_income, base_income_sd))) %>%
  mutate(income = income + income * (gender * income_male_factor)) %>%
  mutate(income = income + income * (ethnicity * income_ethnicity_factor)) %>%
  mutate(gender = factor(map_chr(gender, ~ levels(genders)[.x + 1])),
         ethnicity = factor(map_chr(ethnicity, ~ levels(ethnicities)[.x])))

summary(population)
    gender          ethnicity            first_name        last_name            id        
 Female:939885   Arabic  :529797   Joshua     : 13492   Martinez:  8997   1      :     1  
 Male  : 60115   Asian   :  4500   Michael    : 12631   Smith   :  6119   2      :     1  
                 Black   :121069   Brandon    : 10723   Johnson :  5939   3      :     1  
                 Hispanic: 81132   Christopher:  9875   Garcia  :  5708   4      :     1  
                 Indian  : 27863   Tyler      :  9384   Williams:  4963   5      :     1  
                 White   :235639   Jacob      :  8582   Jones   :  4171   6      :     1  
                                   (Other)    :935313   (Other) :964103   (Other):999994  
     income      
 Min.   : 10340  
 1st Qu.: 19668  
 Median : 28127  
 Mean   : 38746  
 3rd Qu.: 57852  
 Max.   :179331  
                 
population %>%
  ggplot(aes(ethnicity, fill = ethnicity)) +
  geom_bar()


population %>%
  ggplot(aes(gender, fill = gender)) +
  geom_bar()


population %>%
  ggplot(aes(income, fill = ethnicity, lty = gender)) +
  geom_density(alpha = 0.2) +
  facet_wrap(~ ethnicity, ncol = 1)

How much more/less income do males make compared to females?

On average, which ethnicity makes the most?

On average, which ethnicity makes the least?

Are there any significant differences in the proportion of genders?

Are there any significant differences in the proportion of ethnicities?

Defining population potential outcomes (God-mode)

Now that we have a population, we will generate the effects that the policy will have on each individual. Since we have full knowledge and control of this population, we will know the treatment effect for each individual by defining both of their potential outcomes (with and without treatment). The researcher will not know these values, it is their job to recover these values.

effect_size <- 3000
effect_sd <- 1000
income_time_factor <- runif(1)

population <- population %>%
  mutate(potential_0 = income * income_time_factor) %>%
  mutate(potential_1 = round(
    potential_0 + rnorm(population_size, effect_size, effect_sd)))

population %>%
  summarise(across(c(income, potential_0, potential_1), list(`_mean` = mean,
                                                             `_stddev` = sd))) %>%
  pivot_longer(everything(), names_sep = "__", names_to = c("metric", "agg")) %>%
  pivot_wider(names_from = agg)

population %>%
  pivot_longer(c(income, potential_0, potential_1)) %>%
  ggplot(aes(value, fill = name)) +
  geom_density(alpha = 0.2) +
  facet_grid(ethnicity ~ gender, scales = "free")

What the mean and standard deviation of the potential outcomes without treatment?

What the mean and standard deviation of the potential outcomes with treatment?

What is our actual population average treatment effect?

What would have happened to incomes if everyone received treatment?

What would have happened to incomes if no one received treatment?

Sampling from the population (researcher mode)

As a researcher, we usually don’t have access to the entire population given a limited research budget. We typically have to choose a subset of this population. Ideally, we should randomly sample from the group of people from the population that we would like to study in our research.

If the policy or research question under consideration applies to everyone, we’ll want to randomly sample from the entire population. It could also be that the policy only applies to women, or to certain ethnicities, in which case there’s no need to sample from the entire population, only the sub-population (e.g. women or a specific ethnicity) of concern.

sample_sizes <- 10 ^ seq(1, log(population_size, 10))

population_samples <- map_dfr(sample_sizes, function(sample_size) {
  population %>%
    sample_n(sample_size) %>%
    mutate(sample_size = sample_size)
})

population_samples %>%
  ggplot(aes(gender, fill = gender)) +
  geom_bar() +
  facet_wrap(~ sample_size, scales = "free_x") +
  coord_flip()


population_samples %>%
  ggplot(aes(ethnicity, fill = ethnicity)) +
  geom_bar() +
  facet_wrap(~ sample_size, scales = "free_x") +
  coord_flip()


population_samples %>%
  ggplot(aes(income)) +
  geom_histogram(binwidth = 1000) +
  facet_wrap(~ sample_size, scales = "free")

What are the different sample sizes we will work with as a researcher?

What do you notice about the distribution of genders, ethnicities, and incomes as sample size increases?

How does randomly sampling from the population help in estimating the treatment effect of our program?

Estimating the Average Treatment Effect (researcher mode)

Randomly sampling from the population helps ensure our study sample is representative of the population at large. However, randomly sampling from the population does not help us estimate the effect of our policy. In order to estimate the effect of our policy we need to compare Potential Outcomes, specifically the potential outcome when someone gets treatment and the outcome when they do not get treatment? The average of the difference between these two potential outcomes is known as the Average Treatment Effect (ATE).

Does the researcher know the values of both potential outcomes for the individuals in their sample?

Describe, in a few sentences, how you could estimate the ATE from the samples we are given?

Full compliance

In order to be able to estimate the ATE, we need to construct two statistically identical groups with the exception that one of these groups receives treatment while the others do not. This is the core of the Randomized Controlled Trial (RCT).

Certain settings for RCTs ensure that everyone who is in the treatment group gets treatment while no one in the control group receives treatment. This is more difficult to ensure in most cases involving human subjects since human subjects have the freedom to choose to take up the treatment or not. The ability for those in the treatment group to deny treatment and those in the control group to take up treatment is known as non-compliance and the underlying assumption of which types of this non-compliance exists needs to be evaulated on a case-by-case basis.

What are the four different types of individuals in our sample and which of those individuals are assumed to exist and not exist under full compliance?

First we simulate the RCT with each of the sample sizes from above with Full Compliance.

The code below simulates random assignment of treatment to half of the study sample for increasing study sample sizes and tries to calculate the average treatment effect based on what is observed due to treatment assignment.

The first row is a summary row taking the average of all of the individual rows below it. The only thing that changes from table to table is the number of individuals in that sample, notice the number of rows in each table in the bottom left hand corner. Scroll to the right to see all the columns.

invisible(lapply(sample_sizes, function(sample_size) {
  population %>%
    sample_n(sample_size) %>%
    # select(id, first_name, starts_with("potential_")) %>%
    mutate(unit_tx_effect = potential_1 - potential_0) %>%
    arrange(runif(n())) %>%
    mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
    arrange(id) %>%
    mutate(observed_0 = if_else(group == "control", potential_0,
                                        NA_real_),
           observed_1 = if_else(group == "treatment", potential_1,
                                        NA_real_)) %>%
    bind_rows(
      summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
        mutate(across(where(is.numeric), round, digits = 1),
               across(where(is.character), ~ "<<SUMMARY>>"),
               id = NA)) %>%
    mutate(observed_ate = observed_1 - observed_0) %>%
    arrange(desc(row_number())) %>%
  select(-id) %>% print()
}))

Try to explain what each column name is referring to.

Why are there NA values in observed_0 and observed_1 columns?

What is the relationship between potential_0, potential_1, and unit_tx_effect?

What is the relatinoship between observed_0, observed_1 and observed_ate?*

What is our actual treatment effect? What do you notice about the observed ATE as the sample increases?*

Non-compliance

Here we do the same exercise as above but consider the case where we have non-compliers. Specifically we have 60% compliers, 20% never-takers and 20% always-takers. We assume defiers do not exist in our population (not always true). Instead of estimating just the ATE, we’re now estimating the intent-to-treat effect (ITT) and the Complier Average Causal Effect (CACE aka LATE) which is the ATE for the compliers.

Scroll to the right to see all the columns.

#  (3/5 complier, 1/5 nevertaker, 1/5 alwaystaker)
complier_types <- c("alwaystaker", "alwaystaker",
                    "nevertaker", "nevertaker",
                    "complier", "complier", "complier",
                    "complier", "complier", "complier")

invisible(lapply(sample_sizes, function(sample_size) {
  population %>%
    # select sample_size units randomly
    sample_n(sample_size) %>%
    # select client name and potential outcomes
    select(first_name, starts_with("potential_")) %>%
    # calculate true unit-level treatment effect
    mutate(unit_tx_effect = potential_1 - potential_0) %>%
    # randomly assign treatment and control (50/50)
    arrange(runif(n())) %>%
    mutate(group = if_else(row_number() < n() / 2, "treatment", "control")) %>%
    arrange(runif(n())) %>%
    # randomly assign complier types
    # mutate(compliance_group = complier_types[ntile(runif(n()), n = 5)]) %>%
    mutate(compliance_group = complier_types[floor(10 * runif(n())) + 1]) %>%
    # set observed outcomes based on control and treatment assignment
    mutate(observed_0 = if_else(group == "control", potential_0,
                                        NA_real_),
           observed_1 = if_else(group == "treatment", potential_1,
                                        NA_real_)) %>%
    # set observed outcomes based on complier type
    mutate(
      observed_0 = if_else(
        compliance_group == "alwaystaker" & !is.na(observed_0), potential_1,
        observed_0),
      observed_1 = if_else(
        compliance_group == "nevertaker" & !is.na(observed_1), potential_0,
        observed_1)) %>%
    # complier type shares
    mutate(is_taker_tx = if_else(
             group == "treatment",
             compliance_group %in% c("alwaystaker", "complier"), NA),
           is_taker_ctl = if_else(
             group == "control",
             compliance_group %in% c("alwaystaker"), NA)) %>%
    # calculate summaries
    bind_rows(
      summarise(., across(starts_with(c("observed_", "is_")), mean, na.rm = TRUE)) %>%
        mutate(across(where(is.numeric), round, digits = 1), first_name = "<<SUMMARY>>")
    ) %>%
    mutate(observed_itt_effect = observed_1 - observed_0) %>%
    arrange(desc(row_number())) %>%
    mutate(alpha = is_taker_tx - is_taker_ctl,
           cace = observed_itt_effect / alpha) %>%
    print()
}))

Building from your knowledge from the last exercise, describe the new columns in these tables: is_taker_tx, is_taker_ctl, observed_itt_effect, alpha, and cace.

What is the relationship between is_taker_tx, is_taker_ctl, and alpha?

What is the relationship between obs_itt, obs_alpha, and obs_cace? (obs = observed)

What do you notice about alpha and cace as the sample size increases?

Statistical significance

What does statistical significance mean and how does it relate to sample size? We take the simpler case of Full Compliance from above and simulate the random assignment of treatment 100 times:

num_simulations <- 100
study_samples <- map(sample_sizes, ~ population %>% sample_n(.x))

simulation_results <- map_dfr(1:num_simulations, function(x) {
  map_dfr(study_samples, function(study_sample) {
    study_sample %>%
      arrange(runif(n())) %>%
      mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
      arrange(id) %>%
      mutate(observed_0 = if_else(group == "control", potential_0,
                                          NA_real_),
             observed_1 = if_else(group == "treatment", potential_1,
                                          NA_real_)) %>%
      mutate(sample_size = n()) %>%
      summarise(t_test = list(t.test(observed_1, observed_0)),
                across(where(is.numeric), mean, na.rm = TRUE)) %>%
      mutate(observed_ate = observed_1 - observed_0,
             p_value = map_dbl(t_test, ~ .x$p.value),
             confint_left = map_dbl(t_test, ~ .x$conf.int[[1]]),
             confint_right = map_dbl(t_test, ~ .x$conf.int[[2]]))
  })
})

simulation_results %>%
  ggplot(aes(observed_ate)) +
  geom_histogram(binwidth = 100) +
  facet_wrap(~ sample_size, scales = "free_y")


# simulation_results %>%
#   ggplot(aes(p_value, fill = sample_size)) +
#   # geom_density(alpha = 0.1) +
#   geom_histogram(binwidth = 0.01) +
#   facet_wrap(~ sample_size, scales = "free_y")
# 
# simulation_results %>%
#   filter(sample_size == 1000000) %>%
#   select(p_value, confint_left, observed_ate, confint_right) %>%
#   mutate(covered = observed_ate[1] > confint_left & observed_ate[1] < confint_right) %>%
#   summarise(mean(covered), p_value = p_value[1])

What do you notice about the distribution of ATEs we get from the simulations with different sample sizes?

A coding walkthrough

Maybe this all makes sense to you but the coding is still hard to fully understand. That’s understandable! Coding is like any language, it takes a long time to learn, practice and understand. Let’s walk through the code that generated the tables from teh Full Compliance section above: @ref(tab:full-compliance).

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n())) %>%
  mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
  arrange(id) %>%
  mutate(observed_0 = if_else(group == "control", potential_0,
                                      NA_real_),
         observed_1 = if_else(group == "treatment", potential_1,
                                      NA_real_)) %>%
  bind_rows(
    summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
      mutate(across(where(is.numeric), round, digits = 1),
             across(where(is.character), ~ "<<SUMMARY>>"),
             id = NA)) %>%
  mutate(observed_ate = observed_1 - observed_0) %>%
  arrange(desc(row_number())) %>%
  select(-id)

We go through this line by line. First it’s simply the population data frame we generated at the beginning. A data frame is simply data arranged as a table of rows and columns.

population

We define a variable called sample_size and give it a value of 100.

sample_size <- 100

We then randomly sample, in this case, sample_size number of rows from this data frame:

population %>%
  sample_n(sample_size)

Next we mutate the data frame by adding a new column called unit_tx_effect which is defined as potential_1 - potential_0 the difference between the two potential outcomes. The verb mutate simply means that we’re modifying or adding to the data frame with this operation.

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0)

You will notice that this new column is created as the last (right-most) column of this data frame.

Next, we arrange the rows of the data frame by runif(n()).

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n()))

The runif(n()) function simply generates random chosen numbers between 0 and 1 for each row in the data frame. arrange sorts the data frame by this row and we have rows that are now in random order.

You can play with runif() below to see how it works:

runif(10)
 [1] 0.6285034 0.3483613 0.3654089 0.5586069 0.7535734 0.5551276 0.6164481 0.2663728 0.7262539 0.7536303

Why did we order the rows randomly? Well this leads to our next step in mutate-ing the first half of the rows as treatment, and the second half of the rows as control. Since they are randomly ordered, this will create our random assignment of treatment.

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n())) %>%
  mutate(group = if_else(row_number() <= n() / 2, "treatment", "control"))

The if_else(row_number() <= n() / 2, "treatment", "control") expression tells us that if the row number is less than or equal to the total row numbers divided by 2, assign the value treatment otherwise assign the value control. You will notice a new column called group created on the rightmost side of this data frame.

The next arrange simply orders the rows in the original id order, there’s no special reason to do this except keeping our data frame in order of id numbers. The next step defines some more columns in our data frame.

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n())) %>%
  mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
  arrange(id) %>%
  mutate(observed_0 = if_else(group == "control", potential_0,
                                      NA_real_),
         observed_1 = if_else(group == "treatment", potential_1,
                                      NA_real_))

Similar to the treatment vs control assignment to the group column, these two new columns define the observed outcome in the treatment group, observed_1 and the observed outcome in the control group, observed_0. The logic should look familiar:

observed_0 = if_else(group == "control", potential_0, NA_real_) suggests that if the group column has the value control then we should assign observed_0 the value of the potential outcome without treatment, potential_0. Otherwise, the column gets an NA_real_ value which simply means the data is missing since the individual was in the other treatment group. The same applies to the creation of the observed_1 column.

The next chunk of code beginning with bind_rows is fairly complicated and not particularly elegant. I will skip the detailed explanation for now but the purpose of this step is to create a summary row that takes the mean of all the numeric columns of the data and rounds them to the first significant digit. It also replaces columns that are of a non-numeric character type and replaces it will the value <<SUMMARY>>.

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n())) %>%
  mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
  arrange(id) %>%
  mutate(observed_0 = if_else(group == "control", potential_0,
                                      NA_real_),
         observed_1 = if_else(group == "treatment", potential_1,
                                      NA_real_)) %>%
  bind_rows(
    summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
      mutate(across(where(is.numeric), round, digits = 1),
             across(where(is.character), ~ "<<SUMMARY>>"),
             id = NA))

Let’s do a slight modification of the code by taking out the bind_rows function to get an intuition of what might be going on here:

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n())) %>%
  mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
  arrange(id) %>%
  mutate(observed_0 = if_else(group == "control", potential_0,
                                      NA_real_),
         observed_1 = if_else(group == "treatment", potential_1,
                                      NA_real_)) %>%
  summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
  mutate(across(where(is.numeric), round, digits = 1),
         across(where(is.character), ~ "<<SUMMARY>>"),
         id = NA)

We see that we create a single row that averages the values from each numeric column (ignoring the missing NA values) and roudns it to a single significant digit. This row is simply added as the first row in the data frame and then the difference between observed_1 and observed_0 is taken as the observed_ate:

population %>%
  sample_n(sample_size) %>%
  mutate(unit_tx_effect = potential_1 - potential_0) %>%
  arrange(runif(n())) %>%
  mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
  arrange(id) %>%
  mutate(observed_0 = if_else(group == "control", potential_0,
                                      NA_real_),
         observed_1 = if_else(group == "treatment", potential_1,
                                      NA_real_)) %>%
  bind_rows(
    summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
      mutate(across(where(is.numeric), round, digits = 1),
             across(where(is.character), ~ "<<SUMMARY>>"),
             id = NA)) %>%
  mutate(observed_ate = observed_1 - observed_0) %>%
  arrange(desc(row_number())) %>%
  select(-id)

As a finishing touch we reverse the order of the rows so that the newly added summary row is on top, and then remove the id column to reduce the number of columns of the data frame displayed.

LS0tCnRpdGxlOiAiUG90ZW50aWFsIE91dGNvbWVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGlzIG5vdGVib29rIGV4cGxhaW5zIHRoZSBbUG90ZW50aWFsIE91dGNvbWVzIEZyYW1ld29ya10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUnViaW5fY2F1c2FsX21vZGVsKSBhbmQgZXN0aW1hdGlvbiBvZiBDYXVzYWwgRWZmZWN0cywgaW4gcGFydGljdWxhciB0aGUgW0F2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXZlcmFnZV90cmVhdG1lbnRfZWZmZWN0KSBhbmQgcmVsYXRlZCBlc3RpbWF0b3JzOiB0aGUgW0ludGVudC10by1UcmVhdCBFZmZlY3RdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ludGVudGlvbi10by10cmVhdF9hbmFseXNpcykgYW5kIHRoZSBbQ29tcGxpZXIgQXZlcmFnZSBDYXVzYWwgRWZmZWN0XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Mb2NhbF9hdmVyYWdlX3RyZWF0bWVudF9lZmZlY3QpIChhbHNvIGtub3duIGFzIHRoZSBMb2NhbCBBdmVyYWdlIFRyZWF0bWVudCBFZmZlY3QpLgoKVXNpbmcgYSBnZW5lcmF0ZWQgcG9wdWxhdGlvbiBhcyBhIHJlZmVyZW5jZSwgd2UgZXhhbWluZSB0aGUgaW1wbGljYXRpb25zIG9mIHJhbmRvbSBzYW1wbGluZyBhbmQgcmFuZG9tIGFzc2lnbm1lbnQgb2YgdHJlYXRtZW50LCBub24tY29tcGxpYW5jZSwgYW5kIGNvbnNlcXVlbmNlcyBvZiBsaW1pdGVkIHNhbXBsZSBzaXplcy4KClRoZSBjb2RlIG5lZWRlZCB0byBydW4gdGhpcyBhbmFseXNpcyBjYW4gYmUgZmluZCBpbiB0aGUgZm9sbG93aW5nIEdpdEh1YiBbcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL3JvYm90b24vcmFuZG9taXplKSB3aGVyZSB5b3UgY2FuIGRvd25sb2FkLCBjb21tZW50LCBvciBtYWtlIGNvbnRyaWJ1dGlvbnMgb2YgeW91ciBvd24uCgojIyBSZXF1aXJlbWVudHMKCkZpcnN0LCB5b3Ugd2lsbCBuZWVkIFJTdHVkaW8gdG8gcnVuIHRoaXMgbm90ZWJvb2suIFBsZWFzZSBkb3dubG9hZCBpdCBbaGVyZV0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZC8jZG93bmxvYWQpIGFuZCB0aGVuIGluc3RhbGwgaXQuCgpUaGVyZSBhcmUgdHdvIGxpYnJhcmllcyBuZWVkZWQgdG8gcnVuIHRoaXMgUiBOb3RlYm9vazoKMS4gW3RpZHl2ZXJzZV0oaHR0cHM6Ly90aWR5dmVyc2UudGlkeXZlcnNlLm9yZy8pIChmb3IgZGF0YSBtYW5pcHVsYXRpb24pCjIuIFtyYW5kb21OYW1lc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JhbmRvbU5hbWVzL3ZpZ25ldHRlcy9yYW5kb21OYW1lcy5odG1sKSAodG8gZ2VuZXJhdGUgdGhlIHBvcHVsYXRpb24pCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21OYW1lcyIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJhbmRvbU5hbWVzKQpgYGAKCllvdSBtYXkgZXhwZXJpZW5jZSBlcnJvcnMgdHJ5aW5nIHRvIGluc3RhbGwgdGhlc2UgcGFja2FnZXMuIFlvdXIgYmVzdCBmcmllbmQsIGluIHRoaXMgY2FzZSwgaXMgR29vZ2xlLiBTZWFyY2ggZm9yIHRoZSBlcnJvciBtZXNzYWdlcyB5b3UgcmVjZWl2ZSBhbmQgdHJ5IHRvIHRyb3VibGVzaG9vdCB5b3VyIHdheSB0aHJvdWdoIGluc3RhbGxpbmcgdGhlIHBhY2thZ2UuIFVuZm9ydHVuYXRlbHkgdGhlcmUgaXNuJ3QgYSBjb25zaXN0ZW50IHNvbHV0aW9uIHRvIHRoZXNlIHByb2JsZW1zIGFzIGRpZmZlcmVudCBvcGVyYXRpbmcgc3lzdGVtcyBhbmQgbG9jYWwgc2V0dGluZ3MgY2FuIGNhdXNlIGEgdmFyaWV0eSBvZiBpc3N1ZXMuIEhvd2V2ZXIsIGFsbCB0aGVzZSBpc3N1ZXMgc2hvdWxkIGJlIGFibGUgdG8gYmUgcmVzb2x2ZWQuCgojIyBHZW5lcmF0aW5nIHVwIG91ciBwb3B1bGF0aW9uCgpMZXRzIGRlZmluZSBhbmQgZ2VuZXJhdGUgdGhlIHBvcHVsYXRpb24gd2UncmUgZ29pbmcgdG8gc3R1ZHkuIEJlbG93IHdlIHNldCBzb21lIGdlbmVyYWwgY2hhcmFjdGVyaXN0aWNzOgotIHRoZSBzaXplIG9mIHRoZSBwb3B1bGF0aW9uLAotIGdlbmRlciBhbmQgZXRobmljaXR5IGRlbW9ncmFwaGljcyBhbmQKLSB0aGUgYmFzZSBsZXZlbCBpbmNvbWUgYW5kIHZhcmlhdGlvbiAoc3RhbmRhcmQgZGV2aWF0aW9uKSBvZiB0aGF0IGluY29tZS4KCmBgYHtyfQpwb3B1bGF0aW9uX3NpemUgPC0gMTAwMDAwMApiYXNlX2luY29tZSA8LSAxMDAwMApiYXNlX2luY29tZV9zZCA8LSAxMDAwCgpldGhuaWNpdGllcyA8LSBmYWN0b3IoYygiSW5kaWFuIiwgIkFzaWFuIiwgIkJsYWNrIiwgIkhpc3BhbmljIiwgIldoaXRlIiwgIkFyYWJpYyIpKQpnZW5kZXJzIDwtIGZhY3RvcihjKCJGZW1hbGUiLCAiTWFsZSIpKQoKYGBgCgoqKkhvdyBtYW55IHBlb3BsZSBoYXZlIHdlIGRlZmluZWQgdG8gYmUgaW4gb3VyIHBvcHVsYXRpb24/KioKCioqV2hhdCBpcyB0aGVpciBleHBlY3RlZCBiYXNlIGluY29tZT8qKgoKKipIb3cgbWFueSBkaWZmZXJlbnQgZXRobmljaXRpZXMgYXJlIGluIG91ciBwb3B1bGF0aW9uPyoqCgpgYGB7cn0KZGVtb2dyYXBoaWNzIDwtIHRpYmJsZSgKICBnZW5kZXIgPSBzYW1wbGUoZ2VuZGVycywgcG9wdWxhdGlvbl9zaXplLCBwcm9iID0gcnVuaWYobGVuZ3RoKGdlbmRlcnMpKSwKICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9IFRSVUUpLAogIGV0aG5pY2l0eSA9IHNhbXBsZShldGhuaWNpdGllcywgcG9wdWxhdGlvbl9zaXplLCBwcm9iID0gcnVuaWYobGVuZ3RoKGV0aG5pY2l0aWVzKSksCiAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBUUlVFKSkKCmluY29tZV9tYWxlX2ZhY3RvciA8LSBydW5pZigxKQppbmNvbWVfZXRobmljaXR5X2ZhY3RvciA8LSBydW5pZigxKQoKcG9wdWxhdGlvbiA8LSByYW5kb21OYW1lcyhwb3B1bGF0aW9uX3NpemUsIHJldHVybi5jb21wbGV0ZS5kYXRhID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5kZXIgPSBhcy5udW1lcmljKGRlbW9ncmFwaGljcyRnZW5kZXIpIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuaWNpdHkgPSBhcy5udW1lcmljKGRlbW9ncmFwaGljcyRldGhuaWNpdHkpKSAlPiUKICBtdXRhdGUoaWQgPSBmYWN0b3Iocm93X251bWJlcigpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpKSAlPiUKICBtdXRhdGUoaW5jb21lID0gcm91bmQocm5vcm0ocG9wdWxhdGlvbl9zaXplLCBiYXNlX2luY29tZSwgYmFzZV9pbmNvbWVfc2QpKSkgJT4lCiAgbXV0YXRlKGluY29tZSA9IGluY29tZSArIGluY29tZSAqIChnZW5kZXIgKiBpbmNvbWVfbWFsZV9mYWN0b3IpKSAlPiUKICBtdXRhdGUoaW5jb21lID0gaW5jb21lICsgaW5jb21lICogKGV0aG5pY2l0eSAqIGluY29tZV9ldGhuaWNpdHlfZmFjdG9yKSkgJT4lCiAgbXV0YXRlKGdlbmRlciA9IGZhY3RvcihtYXBfY2hyKGdlbmRlciwgfiBsZXZlbHMoZ2VuZGVycylbLnggKyAxXSkpLAogICAgICAgICBldGhuaWNpdHkgPSBmYWN0b3IobWFwX2NocihldGhuaWNpdHksIH4gbGV2ZWxzKGV0aG5pY2l0aWVzKVsueF0pKSkKCnN1bW1hcnkocG9wdWxhdGlvbikKCnBvcHVsYXRpb24gJT4lCiAgZ2dwbG90KGFlcyhldGhuaWNpdHksIGZpbGwgPSBldGhuaWNpdHkpKSArCiAgZ2VvbV9iYXIoKQoKcG9wdWxhdGlvbiAlPiUKICBnZ3Bsb3QoYWVzKGdlbmRlciwgZmlsbCA9IGdlbmRlcikpICsKICBnZW9tX2JhcigpCgpwb3B1bGF0aW9uICU+JQogIGdncGxvdChhZXMoaW5jb21lLCBmaWxsID0gZXRobmljaXR5LCBsdHkgPSBnZW5kZXIpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArCiAgZmFjZXRfd3JhcCh+IGV0aG5pY2l0eSwgbmNvbCA9IDEpCmBgYAoKKipIb3cgbXVjaCBtb3JlL2xlc3MgaW5jb21lIGRvIG1hbGVzIG1ha2UgY29tcGFyZWQgdG8gZmVtYWxlcz8qKgoKKipPbiBhdmVyYWdlLCB3aGljaCBldGhuaWNpdHkgbWFrZXMgdGhlIG1vc3Q/KioKCioqT24gYXZlcmFnZSwgd2hpY2ggZXRobmljaXR5IG1ha2VzIHRoZSBsZWFzdD8qKgoKKipBcmUgdGhlcmUgYW55IHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIHRoZSBwcm9wb3J0aW9uIG9mIGdlbmRlcnM/KioKCioqQXJlIHRoZXJlIGFueSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBpbiB0aGUgcHJvcG9ydGlvbiBvZiBldGhuaWNpdGllcz8qKgoKIyMgRGVmaW5pbmcgcG9wdWxhdGlvbiBwb3RlbnRpYWwgb3V0Y29tZXMgKEdvZC1tb2RlKQoKTm93IHRoYXQgd2UgaGF2ZSBhIHBvcHVsYXRpb24sIHdlIHdpbGwgZ2VuZXJhdGUgdGhlIGVmZmVjdHMgdGhhdCB0aGUgcG9saWN5IF93aWxsXyBoYXZlIG9uIGVhY2ggaW5kaXZpZHVhbC4gU2luY2Ugd2UgaGF2ZSBmdWxsIGtub3dsZWRnZSBhbmQgY29udHJvbCBvZiB0aGlzIHBvcHVsYXRpb24sIHdlIHdpbGwga25vdyB0aGUgdHJlYXRtZW50IGVmZmVjdCBmb3IgZWFjaCBpbmRpdmlkdWFsIGJ5IGRlZmluaW5nIGJvdGggb2YgdGhlaXIgcG90ZW50aWFsIG91dGNvbWVzICh3aXRoIGFuZCB3aXRob3V0IHRyZWF0bWVudCkuIFRoZSByZXNlYXJjaGVyIHdpbGwgX25vdF8ga25vdyB0aGVzZSB2YWx1ZXMsIGl0IGlzIHRoZWlyIGpvYiB0byByZWNvdmVyIHRoZXNlIHZhbHVlcy4KCmBgYHtyfQplZmZlY3Rfc2l6ZSA8LSAzMDAwCmVmZmVjdF9zZCA8LSAxMDAwCmluY29tZV90aW1lX2ZhY3RvciA8LSBydW5pZigxKQoKcG9wdWxhdGlvbiA8LSBwb3B1bGF0aW9uICU+JQogIG11dGF0ZShwb3RlbnRpYWxfMCA9IGluY29tZSAqIGluY29tZV90aW1lX2ZhY3RvcikgJT4lCiAgbXV0YXRlKHBvdGVudGlhbF8xID0gcm91bmQoCiAgICBwb3RlbnRpYWxfMCArIHJub3JtKHBvcHVsYXRpb25fc2l6ZSwgZWZmZWN0X3NpemUsIGVmZmVjdF9zZCkpKQoKcG9wdWxhdGlvbiAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKGMoaW5jb21lLCBwb3RlbnRpYWxfMCwgcG90ZW50aWFsXzEpLCBsaXN0KGBfbWVhbmAgPSBtZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYF9zdGRkZXZgID0gc2QpKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfc2VwID0gIl9fIiwgbmFtZXNfdG8gPSBjKCJtZXRyaWMiLCAiYWdnIikpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBhZ2cpCgpwb3B1bGF0aW9uICU+JQogIHBpdm90X2xvbmdlcihjKGluY29tZSwgcG90ZW50aWFsXzAsIHBvdGVudGlhbF8xKSkgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSwgZmlsbCA9IG5hbWUpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArCiAgZmFjZXRfZ3JpZChldGhuaWNpdHkgfiBnZW5kZXIsIHNjYWxlcyA9ICJmcmVlIikKYGBgCgoqKldoYXQgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWVzIHdpdGhvdXQgdHJlYXRtZW50PyoqCgoqKldoYXQgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWVzIHdpdGggdHJlYXRtZW50PyoqCgoqKldoYXQgaXMgb3VyIGFjdHVhbCBwb3B1bGF0aW9uIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdD8qKgoKKipXaGF0IHdvdWxkIGhhdmUgaGFwcGVuZWQgdG8gaW5jb21lcyBpZiBldmVyeW9uZSByZWNlaXZlZCB0cmVhdG1lbnQ/KioKCioqV2hhdCB3b3VsZCBoYXZlIGhhcHBlbmVkIHRvIGluY29tZXMgaWYgbm8gb25lIHJlY2VpdmVkIHRyZWF0bWVudD8qKgoKIyMgU2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiAocmVzZWFyY2hlciBtb2RlKQoKQXMgYSByZXNlYXJjaGVyLCB3ZSB1c3VhbGx5IGRvbid0IGhhdmUgYWNjZXNzIHRvIHRoZSBlbnRpcmUgcG9wdWxhdGlvbiBnaXZlbiBhIGxpbWl0ZWQgcmVzZWFyY2ggYnVkZ2V0LiBXZSB0eXBpY2FsbHkgaGF2ZSB0byBjaG9vc2UgYSBzdWJzZXQgb2YgdGhpcyBwb3B1bGF0aW9uLiBJZGVhbGx5LCB3ZSBzaG91bGQgcmFuZG9tbHkgc2FtcGxlIGZyb20gdGhlIGdyb3VwIG9mIHBlb3BsZSBmcm9tIHRoZSBwb3B1bGF0aW9uIHRoYXQgd2Ugd291bGQgbGlrZSB0byBzdHVkeSBpbiBvdXIgcmVzZWFyY2guCgpJZiB0aGUgcG9saWN5IG9yIHJlc2VhcmNoIHF1ZXN0aW9uIHVuZGVyIGNvbnNpZGVyYXRpb24gYXBwbGllcyB0byBldmVyeW9uZSwgd2UnbGwgd2FudCB0byByYW5kb21seSBzYW1wbGUgZnJvbSB0aGUgZW50aXJlIHBvcHVsYXRpb24uIEl0IGNvdWxkIGFsc28gYmUgdGhhdCB0aGUgcG9saWN5IG9ubHkgYXBwbGllcyB0byB3b21lbiwgb3IgdG8gY2VydGFpbiBldGhuaWNpdGllcywgaW4gd2hpY2ggY2FzZSB0aGVyZSdzIG5vIG5lZWQgdG8gc2FtcGxlIGZyb20gdGhlIGVudGlyZSBwb3B1bGF0aW9uLCBvbmx5IHRoZSBzdWItcG9wdWxhdGlvbiAoZS5nLiB3b21lbiBvciBhIHNwZWNpZmljIGV0aG5pY2l0eSkgb2YgY29uY2Vybi4KCmBgYHtyfQpzYW1wbGVfc2l6ZXMgPC0gMTAgXiBzZXEoMSwgbG9nKHBvcHVsYXRpb25fc2l6ZSwgMTApKQoKcG9wdWxhdGlvbl9zYW1wbGVzIDwtIG1hcF9kZnIoc2FtcGxlX3NpemVzLCBmdW5jdGlvbihzYW1wbGVfc2l6ZSkgewogIHBvcHVsYXRpb24gJT4lCiAgICBzYW1wbGVfbihzYW1wbGVfc2l6ZSkgJT4lCiAgICBtdXRhdGUoc2FtcGxlX3NpemUgPSBzYW1wbGVfc2l6ZSkKfSkKCnBvcHVsYXRpb25fc2FtcGxlcyAlPiUKICBnZ3Bsb3QoYWVzKGdlbmRlciwgZmlsbCA9IGdlbmRlcikpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH4gc2FtcGxlX3NpemUsIHNjYWxlcyA9ICJmcmVlX3giKSArCiAgY29vcmRfZmxpcCgpCgpwb3B1bGF0aW9uX3NhbXBsZXMgJT4lCiAgZ2dwbG90KGFlcyhldGhuaWNpdHksIGZpbGwgPSBldGhuaWNpdHkpKSArCiAgZ2VvbV9iYXIoKSArCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9zaXplLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGNvb3JkX2ZsaXAoKQoKcG9wdWxhdGlvbl9zYW1wbGVzICU+JQogIGdncGxvdChhZXMoaW5jb21lKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwMCkgKwogIGZhY2V0X3dyYXAofiBzYW1wbGVfc2l6ZSwgc2NhbGVzID0gImZyZWUiKQpgYGAKCioqV2hhdCBhcmUgdGhlIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMgd2Ugd2lsbCB3b3JrIHdpdGggYXMgYSByZXNlYXJjaGVyPyoqCgoqKldoYXQgZG8geW91IG5vdGljZSBhYm91dCB0aGUgZGlzdHJpYnV0aW9uIG9mIGdlbmRlcnMsIGV0aG5pY2l0aWVzLCBhbmQgaW5jb21lcyBhcyBzYW1wbGUgc2l6ZSBpbmNyZWFzZXM/KioKCioqSG93IGRvZXMgcmFuZG9tbHkgc2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiBoZWxwIGluIGVzdGltYXRpbmcgdGhlIHRyZWF0bWVudCBlZmZlY3Qgb2Ygb3VyIHByb2dyYW0/KioKCiMjIEVzdGltYXRpbmcgdGhlIEF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCAocmVzZWFyY2hlciBtb2RlKQoKUmFuZG9tbHkgc2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiBoZWxwcyBlbnN1cmUgb3VyIHN0dWR5IHNhbXBsZSBpcyByZXByZXNlbnRhdGl2ZSBvZiB0aGUgcG9wdWxhdGlvbiBhdCBsYXJnZS4gSG93ZXZlciwgcmFuZG9tbHkgc2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiBkb2VzIF9ub3RfIGhlbHAgdXMgZXN0aW1hdGUgdGhlIGVmZmVjdCBvZiBvdXIgcG9saWN5LiBJbiBvcmRlciB0byBlc3RpbWF0ZSB0aGUgZWZmZWN0IG9mIG91ciBwb2xpY3kgd2UgbmVlZCB0byBjb21wYXJlIF9Qb3RlbnRpYWwgT3V0Y29tZXNfLCBzcGVjaWZpY2FsbHkgdGhlIHBvdGVudGlhbCBvdXRjb21lIHdoZW4gc29tZW9uZSBnZXRzIHRyZWF0bWVudCBhbmQgdGhlIG91dGNvbWUgd2hlbiB0aGV5IGRvIG5vdCBnZXQgdHJlYXRtZW50PyBUaGUgYXZlcmFnZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHR3byBwb3RlbnRpYWwgb3V0Y29tZXMgaXMga25vd24gYXMgdGhlIEF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCAoQVRFKS4KCioqRG9lcyB0aGUgcmVzZWFyY2hlciBrbm93IHRoZSB2YWx1ZXMgb2YgYm90aCBwb3RlbnRpYWwgb3V0Y29tZXMgZm9yIHRoZSBpbmRpdmlkdWFscyBpbiB0aGVpciBzYW1wbGU/KioKCioqRGVzY3JpYmUsIGluIGEgZmV3IHNlbnRlbmNlcywgaG93IHlvdSBjb3VsZCBlc3RpbWF0ZSB0aGUgQVRFIGZyb20gdGhlIHNhbXBsZXMgd2UgYXJlIGdpdmVuPyoqCgojIyMgRnVsbCBjb21wbGlhbmNlCgpJbiBvcmRlciB0byBiZSBhYmxlIHRvIGVzdGltYXRlIHRoZSBBVEUsIHdlIG5lZWQgdG8gY29uc3RydWN0IHR3byBzdGF0aXN0aWNhbGx5IGlkZW50aWNhbCBncm91cHMgd2l0aCB0aGUgZXhjZXB0aW9uIHRoYXQgb25lIG9mIHRoZXNlIGdyb3VwcyByZWNlaXZlcyB0cmVhdG1lbnQgd2hpbGUgdGhlIG90aGVycyBkbyBub3QuIFRoaXMgaXMgdGhlIGNvcmUgb2YgdGhlIFtSYW5kb21pemVkIENvbnRyb2xsZWQgVHJpYWxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JhbmRvbWl6ZWRfY29udHJvbGxlZF90cmlhbCkgKFJDVCkuCgpDZXJ0YWluIHNldHRpbmdzIGZvciBSQ1RzIGVuc3VyZSB0aGF0IGV2ZXJ5b25lIHdobyBpcyBpbiB0aGUgdHJlYXRtZW50IGdyb3VwIGdldHMgdHJlYXRtZW50IHdoaWxlIG5vIG9uZSBpbiB0aGUgY29udHJvbCBncm91cCByZWNlaXZlcyB0cmVhdG1lbnQuIFRoaXMgaXMgbW9yZSBkaWZmaWN1bHQgdG8gZW5zdXJlIGluIG1vc3QgY2FzZXMgaW52b2x2aW5nIGh1bWFuIHN1YmplY3RzIHNpbmNlIGh1bWFuIHN1YmplY3RzIGhhdmUgdGhlIGZyZWVkb20gdG8gY2hvb3NlIHRvIHRha2UgdXAgdGhlIHRyZWF0bWVudCBvciBub3QuIFRoZSBhYmlsaXR5IGZvciB0aG9zZSBpbiB0aGUgdHJlYXRtZW50IGdyb3VwIHRvIGRlbnkgdHJlYXRtZW50IGFuZCB0aG9zZSBpbiB0aGUgY29udHJvbCBncm91cCB0byB0YWtlIHVwIHRyZWF0bWVudCBpcyBrbm93biBhcyBub24tY29tcGxpYW5jZSBhbmQgdGhlIHVuZGVybHlpbmcgYXNzdW1wdGlvbiBvZiB3aGljaCB0eXBlcyBvZiB0aGlzIG5vbi1jb21wbGlhbmNlIGV4aXN0cyBuZWVkcyB0byBiZSBldmF1bGF0ZWQgb24gYSBjYXNlLWJ5LWNhc2UgYmFzaXMuCgoqKldoYXQgYXJlIHRoZSBmb3VyIGRpZmZlcmVudCB0eXBlcyBvZiBpbmRpdmlkdWFscyBpbiBvdXIgc2FtcGxlIGFuZCB3aGljaCBvZiB0aG9zZSBpbmRpdmlkdWFscyBhcmUgYXNzdW1lZCB0byBleGlzdCBhbmQgbm90IGV4aXN0IHVuZGVyIGZ1bGwgY29tcGxpYW5jZT8qKgoKRmlyc3Qgd2Ugc2ltdWxhdGUgdGhlIFJDVCB3aXRoIGVhY2ggb2YgdGhlIHNhbXBsZSBzaXplcyBmcm9tIGFib3ZlIHdpdGggX0Z1bGwgQ29tcGxpYW5jZV8uCgpUaGUgY29kZSBiZWxvdyBzaW11bGF0ZXMgcmFuZG9tIGFzc2lnbm1lbnQgb2YgdHJlYXRtZW50IHRvIGhhbGYgb2YgdGhlIHN0dWR5IHNhbXBsZSBmb3IgaW5jcmVhc2luZyBzdHVkeSBzYW1wbGUgc2l6ZXMgYW5kIHRyaWVzIHRvIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGJhc2VkIG9uIHdoYXQgaXMgb2JzZXJ2ZWQgZHVlIHRvIHRyZWF0bWVudCBhc3NpZ25tZW50LgoKVGhlIGZpcnN0IHJvdyBpcyBhIHN1bW1hcnkgcm93IHRha2luZyB0aGUgYXZlcmFnZSBvZiBhbGwgb2YgdGhlIGluZGl2aWR1YWwgcm93cyBiZWxvdyBpdC4gVGhlIG9ubHkgdGhpbmcgdGhhdCBjaGFuZ2VzIGZyb20gdGFibGUgdG8gdGFibGUgaXMgdGhlIG51bWJlciBvZiBpbmRpdmlkdWFscyBpbiB0aGF0IHNhbXBsZSwgbm90aWNlIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIHRhYmxlIGluIHRoZSBib3R0b20gbGVmdCBoYW5kIGNvcm5lci4gU2Nyb2xsIHRvIHRoZSByaWdodCB0byBzZWUgYWxsIHRoZSBjb2x1bW5zLgoKYGBge3IgZnVsbC1jb21wbGlhbmNlfQppbnZpc2libGUobGFwcGx5KHNhbXBsZV9zaXplcywgZnVuY3Rpb24oc2FtcGxlX3NpemUpIHsKICBwb3B1bGF0aW9uICU+JQogICAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogICAgIyBzZWxlY3QoaWQsIGZpcnN0X25hbWUsIHN0YXJ0c193aXRoKCJwb3RlbnRpYWxfIikpICU+JQogICAgbXV0YXRlKHVuaXRfdHhfZWZmZWN0ID0gcG90ZW50aWFsXzEgLSBwb3RlbnRpYWxfMCkgJT4lCiAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogICAgYXJyYW5nZShpZCkgJT4lCiAgICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgICBvYnNlcnZlZF8xID0gaWZfZWxzZShncm91cCA9PSAidHJlYXRtZW50IiwgcG90ZW50aWFsXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogICAgYmluZF9yb3dzKAogICAgICBzdW1tYXJpc2UoLiwgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCB+ICI8PFNVTU1BUlk+PiIpLAogICAgICAgICAgICAgICBpZCA9IE5BKSkgJT4lCiAgICBtdXRhdGUob2JzZXJ2ZWRfYXRlID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogICAgYXJyYW5nZShkZXNjKHJvd19udW1iZXIoKSkpICU+JQogIHNlbGVjdCgtaWQpICU+JSBwcmludCgpCn0pKQpgYGAKCioqVHJ5IHRvIGV4cGxhaW4gd2hhdCBlYWNoIGNvbHVtbiBuYW1lIGlzIHJlZmVycmluZyB0by4qKgoKKipXaHkgYXJlIHRoZXJlIE5BIHZhbHVlcyBpbiBvYnNlcnZlZF8wIGFuZCBvYnNlcnZlZF8xIGNvbHVtbnM/KioKCioqV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcG90ZW50aWFsXzAsIHBvdGVudGlhbF8xLCBhbmQgdW5pdF90eF9lZmZlY3Q/KioKCioqV2hhdCBpcyB0aGUgcmVsYXRpbm9zaGlwIGJldHdlZW4gb2JzZXJ2ZWRfMCwgb2JzZXJ2ZWRfMSBhbmQgb2JzZXJ2ZWRfYXRlPyoqKgoKKipXaGF0IGlzIG91ciBhY3R1YWwgdHJlYXRtZW50IGVmZmVjdD8gV2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IHRoZSBvYnNlcnZlZCBBVEUgYXMgdGhlIHNhbXBsZSBpbmNyZWFzZXM/KioqCgojIyMgTm9uLWNvbXBsaWFuY2UgCgpIZXJlIHdlIGRvIHRoZSBzYW1lIGV4ZXJjaXNlIGFzIGFib3ZlIGJ1dCBjb25zaWRlciB0aGUgY2FzZSB3aGVyZSB3ZSBoYXZlIG5vbi1jb21wbGllcnMuIFNwZWNpZmljYWxseSB3ZSBoYXZlIDYwJSBjb21wbGllcnMsIDIwJSBuZXZlci10YWtlcnMgYW5kIDIwJSBhbHdheXMtdGFrZXJzLiBXZSBhc3N1bWUgZGVmaWVycyBkbyBub3QgZXhpc3QgaW4gb3VyIHBvcHVsYXRpb24gKG5vdCBhbHdheXMgdHJ1ZSkuIEluc3RlYWQgb2YgZXN0aW1hdGluZyBqdXN0IHRoZSBBVEUsIHdlJ3JlIG5vdyBlc3RpbWF0aW5nIHRoZSBpbnRlbnQtdG8tdHJlYXQgZWZmZWN0IChJVFQpIGFuZCB0aGUgQ29tcGxpZXIgQXZlcmFnZSBDYXVzYWwgRWZmZWN0IChDQUNFIGFrYSBMQVRFKSB3aGljaCBpcyB0aGUgQVRFIGZvciB0aGUgY29tcGxpZXJzLgoKU2Nyb2xsIHRvIHRoZSByaWdodCB0byBzZWUgYWxsIHRoZSBjb2x1bW5zLgoKYGBge3J9CiMgICgzLzUgY29tcGxpZXIsIDEvNSBuZXZlcnRha2VyLCAxLzUgYWx3YXlzdGFrZXIpCmNvbXBsaWVyX3R5cGVzIDwtIGMoImFsd2F5c3Rha2VyIiwgImFsd2F5c3Rha2VyIiwKICAgICAgICAgICAgICAgICAgICAibmV2ZXJ0YWtlciIsICJuZXZlcnRha2VyIiwKICAgICAgICAgICAgICAgICAgICAiY29tcGxpZXIiLCAiY29tcGxpZXIiLCAiY29tcGxpZXIiLAogICAgICAgICAgICAgICAgICAgICJjb21wbGllciIsICJjb21wbGllciIsICJjb21wbGllciIpCgppbnZpc2libGUobGFwcGx5KHNhbXBsZV9zaXplcywgZnVuY3Rpb24oc2FtcGxlX3NpemUpIHsKICBwb3B1bGF0aW9uICU+JQogICAgIyBzZWxlY3Qgc2FtcGxlX3NpemUgdW5pdHMgcmFuZG9tbHkKICAgIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICAgICMgc2VsZWN0IGNsaWVudCBuYW1lIGFuZCBwb3RlbnRpYWwgb3V0Y29tZXMKICAgIHNlbGVjdChmaXJzdF9uYW1lLCBzdGFydHNfd2l0aCgicG90ZW50aWFsXyIpKSAlPiUKICAgICMgY2FsY3VsYXRlIHRydWUgdW5pdC1sZXZlbCB0cmVhdG1lbnQgZWZmZWN0CiAgICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICAgICMgcmFuZG9tbHkgYXNzaWduIHRyZWF0bWVudCBhbmQgY29udHJvbCAoNTAvNTApCiAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPCBuKCkgLyAyLCAidHJlYXRtZW50IiwgImNvbnRyb2wiKSkgJT4lCiAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgIyByYW5kb21seSBhc3NpZ24gY29tcGxpZXIgdHlwZXMKICAgICMgbXV0YXRlKGNvbXBsaWFuY2VfZ3JvdXAgPSBjb21wbGllcl90eXBlc1tudGlsZShydW5pZihuKCkpLCBuID0gNSldKSAlPiUKICAgIG11dGF0ZShjb21wbGlhbmNlX2dyb3VwID0gY29tcGxpZXJfdHlwZXNbZmxvb3IoMTAgKiBydW5pZihuKCkpKSArIDFdKSAlPiUKICAgICMgc2V0IG9ic2VydmVkIG91dGNvbWVzIGJhc2VkIG9uIGNvbnRyb2wgYW5kIHRyZWF0bWVudCBhc3NpZ25tZW50CiAgICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgICBvYnNlcnZlZF8xID0gaWZfZWxzZShncm91cCA9PSAidHJlYXRtZW50IiwgcG90ZW50aWFsXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogICAgIyBzZXQgb2JzZXJ2ZWQgb3V0Y29tZXMgYmFzZWQgb24gY29tcGxpZXIgdHlwZQogICAgbXV0YXRlKAogICAgICBvYnNlcnZlZF8wID0gaWZfZWxzZSgKICAgICAgICBjb21wbGlhbmNlX2dyb3VwID09ICJhbHdheXN0YWtlciIgJiAhaXMubmEob2JzZXJ2ZWRfMCksIHBvdGVudGlhbF8xLAogICAgICAgIG9ic2VydmVkXzApLAogICAgICBvYnNlcnZlZF8xID0gaWZfZWxzZSgKICAgICAgICBjb21wbGlhbmNlX2dyb3VwID09ICJuZXZlcnRha2VyIiAmICFpcy5uYShvYnNlcnZlZF8xKSwgcG90ZW50aWFsXzAsCiAgICAgICAgb2JzZXJ2ZWRfMSkpICU+JQogICAgIyBjb21wbGllciB0eXBlIHNoYXJlcwogICAgbXV0YXRlKGlzX3Rha2VyX3R4ID0gaWZfZWxzZSgKICAgICAgICAgICAgIGdyb3VwID09ICJ0cmVhdG1lbnQiLAogICAgICAgICAgICAgY29tcGxpYW5jZV9ncm91cCAlaW4lIGMoImFsd2F5c3Rha2VyIiwgImNvbXBsaWVyIiksIE5BKSwKICAgICAgICAgICBpc190YWtlcl9jdGwgPSBpZl9lbHNlKAogICAgICAgICAgICAgZ3JvdXAgPT0gImNvbnRyb2wiLAogICAgICAgICAgICAgY29tcGxpYW5jZV9ncm91cCAlaW4lIGMoImFsd2F5c3Rha2VyIiksIE5BKSkgJT4lCiAgICAjIGNhbGN1bGF0ZSBzdW1tYXJpZXMKICAgIGJpbmRfcm93cygKICAgICAgc3VtbWFyaXNlKC4sIGFjcm9zcyhzdGFydHNfd2l0aChjKCJvYnNlcnZlZF8iLCAiaXNfIikpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksIGZpcnN0X25hbWUgPSAiPDxTVU1NQVJZPj4iKQogICAgKSAlPiUKICAgIG11dGF0ZShvYnNlcnZlZF9pdHRfZWZmZWN0ID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogICAgYXJyYW5nZShkZXNjKHJvd19udW1iZXIoKSkpICU+JQogICAgbXV0YXRlKGFscGhhID0gaXNfdGFrZXJfdHggLSBpc190YWtlcl9jdGwsCiAgICAgICAgICAgY2FjZSA9IG9ic2VydmVkX2l0dF9lZmZlY3QgLyBhbHBoYSkgJT4lCiAgICBwcmludCgpCn0pKQpgYGAKCioqQnVpbGRpbmcgZnJvbSB5b3VyIGtub3dsZWRnZSBmcm9tIHRoZSBsYXN0IGV4ZXJjaXNlLCBkZXNjcmliZSB0aGUgbmV3IGNvbHVtbnMgaW4gdGhlc2UgdGFibGVzOiBpc190YWtlcl90eCwgaXNfdGFrZXJfY3RsLCBvYnNlcnZlZF9pdHRfZWZmZWN0LCBhbHBoYSwgYW5kIGNhY2UuKioKCioqV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaXNfdGFrZXJfdHgsIGlzX3Rha2VyX2N0bCwgYW5kIGFscGhhPyoqCgoqKldoYXQgaXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG9ic19pdHQsIG9ic19hbHBoYSwgYW5kIG9ic19jYWNlPyAob2JzID0gb2JzZXJ2ZWQpKioKCioqV2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IGFscGhhIGFuZCBjYWNlIGFzIHRoZSBzYW1wbGUgc2l6ZSBpbmNyZWFzZXM/KioKCiMjIFN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQoKV2hhdCBkb2VzIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBtZWFuIGFuZCBob3cgZG9lcyBpdCByZWxhdGUgdG8gc2FtcGxlIHNpemU/IFdlIHRha2UgdGhlIHNpbXBsZXIgY2FzZSBvZiBGdWxsIENvbXBsaWFuY2UgZnJvbSBhYm92ZSBhbmQgc2ltdWxhdGUgdGhlIHJhbmRvbSBhc3NpZ25tZW50IG9mIHRyZWF0bWVudCAxMDAgdGltZXM6CgpgYGB7cn0KbnVtX3NpbXVsYXRpb25zIDwtIDEwMApzdHVkeV9zYW1wbGVzIDwtIG1hcChzYW1wbGVfc2l6ZXMsIH4gcG9wdWxhdGlvbiAlPiUgc2FtcGxlX24oLngpKQoKc2ltdWxhdGlvbl9yZXN1bHRzIDwtIG1hcF9kZnIoMTpudW1fc2ltdWxhdGlvbnMsIGZ1bmN0aW9uKHgpIHsKICBtYXBfZGZyKHN0dWR5X3NhbXBsZXMsIGZ1bmN0aW9uKHN0dWR5X3NhbXBsZSkgewogICAgc3R1ZHlfc2FtcGxlICU+JQogICAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgICBtdXRhdGUoZ3JvdXAgPSBpZl9lbHNlKHJvd19udW1iZXIoKSA8PSBuKCkgLyAyLCAidHJlYXRtZW50IiwgImNvbnRyb2wiKSkgJT4lCiAgICAgIGFycmFuZ2UoaWQpICU+JQogICAgICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkFfcmVhbF8pLAogICAgICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogICAgICBtdXRhdGUoc2FtcGxlX3NpemUgPSBuKCkpICU+JQogICAgICBzdW1tYXJpc2UodF90ZXN0ID0gbGlzdCh0LnRlc3Qob2JzZXJ2ZWRfMSwgb2JzZXJ2ZWRfMCkpLAogICAgICAgICAgICAgICAgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgbXV0YXRlKG9ic2VydmVkX2F0ZSA9IG9ic2VydmVkXzEgLSBvYnNlcnZlZF8wLAogICAgICAgICAgICAgcF92YWx1ZSA9IG1hcF9kYmwodF90ZXN0LCB+IC54JHAudmFsdWUpLAogICAgICAgICAgICAgY29uZmludF9sZWZ0ID0gbWFwX2RibCh0X3Rlc3QsIH4gLngkY29uZi5pbnRbWzFdXSksCiAgICAgICAgICAgICBjb25maW50X3JpZ2h0ID0gbWFwX2RibCh0X3Rlc3QsIH4gLngkY29uZi5pbnRbWzJdXSkpCiAgfSkKfSkKCnNpbXVsYXRpb25fcmVzdWx0cyAlPiUKICBnZ3Bsb3QoYWVzKG9ic2VydmVkX2F0ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMCkgKwogIGZhY2V0X3dyYXAofiBzYW1wbGVfc2l6ZSwgc2NhbGVzID0gImZyZWVfeSIpCgojIHNpbXVsYXRpb25fcmVzdWx0cyAlPiUKIyAgIGdncGxvdChhZXMocF92YWx1ZSwgZmlsbCA9IHNhbXBsZV9zaXplKSkgKwojICAgIyBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpICsKIyAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSkgKwojICAgZmFjZXRfd3JhcCh+IHNhbXBsZV9zaXplLCBzY2FsZXMgPSAiZnJlZV95IikKIyAKIyBzaW11bGF0aW9uX3Jlc3VsdHMgJT4lCiMgICBmaWx0ZXIoc2FtcGxlX3NpemUgPT0gMTAwMDAwMCkgJT4lCiMgICBzZWxlY3QocF92YWx1ZSwgY29uZmludF9sZWZ0LCBvYnNlcnZlZF9hdGUsIGNvbmZpbnRfcmlnaHQpICU+JQojICAgbXV0YXRlKGNvdmVyZWQgPSBvYnNlcnZlZF9hdGVbMV0gPiBjb25maW50X2xlZnQgJiBvYnNlcnZlZF9hdGVbMV0gPCBjb25maW50X3JpZ2h0KSAlPiUKIyAgIHN1bW1hcmlzZShtZWFuKGNvdmVyZWQpLCBwX3ZhbHVlID0gcF92YWx1ZVsxXSkKYGBgCgoqKldoYXQgZG8geW91IG5vdGljZSBhYm91dCB0aGUgZGlzdHJpYnV0aW9uIG9mIEFURXMgd2UgZ2V0IGZyb20gdGhlIHNpbXVsYXRpb25zIHdpdGggZGlmZmVyZW50IHNhbXBsZSBzaXplcz8qKgoKIyMgQSBjb2Rpbmcgd2Fsa3Rocm91Z2gKCk1heWJlIHRoaXMgYWxsIG1ha2VzIHNlbnNlIHRvIHlvdSBidXQgdGhlIGNvZGluZyBpcyBzdGlsbCBoYXJkIHRvIGZ1bGx5IHVuZGVyc3RhbmQuIFRoYXQncyB1bmRlcnN0YW5kYWJsZSEgQ29kaW5nIGlzIGxpa2UgYW55IGxhbmd1YWdlLCBpdCB0YWtlcyBhIGxvbmcgdGltZSB0byBsZWFybiwgcHJhY3RpY2UgYW5kIHVuZGVyc3RhbmQuIExldCdzIHdhbGsgdGhyb3VnaCB0aGUgY29kZSB0aGF0IGdlbmVyYXRlZCB0aGUgdGFibGVzIGZyb20gdGVoIEZ1bGwgQ29tcGxpYW5jZSBzZWN0aW9uIGFib3ZlOiBcQHJlZih0YWI6ZnVsbC1jb21wbGlhbmNlKS4KCmBgYApwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogIG11dGF0ZShncm91cCA9IGlmX2Vsc2Uocm93X251bWJlcigpIDw9IG4oKSAvIDIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKSAlPiUKICBhcnJhbmdlKGlkKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXyksCiAgICAgICAgIG9ic2VydmVkXzEgPSBpZl9lbHNlKGdyb3VwID09ICJ0cmVhdG1lbnQiLCBwb3RlbnRpYWxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogIGJpbmRfcm93cygKICAgIHN1bW1hcmlzZSguLCBhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIG1lYW4sIG5hLnJtID0gVFJVRSkpICU+JQogICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksCiAgICAgICAgICAgICBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiAiPDxTVU1NQVJZPj4iKSwKICAgICAgICAgICAgIGlkID0gTkEpKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfYXRlID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogIGFycmFuZ2UoZGVzYyhyb3dfbnVtYmVyKCkpKSAlPiUKICBzZWxlY3QoLWlkKQpgYGAKCldlIGdvIHRocm91Z2ggdGhpcyBsaW5lIGJ5IGxpbmUuIEZpcnN0IGl0J3Mgc2ltcGx5IHRoZSBwb3B1bGF0aW9uIGRhdGEgZnJhbWUgd2UgZ2VuZXJhdGVkIGF0IHRoZSBiZWdpbm5pbmcuIEEgYGRhdGEgZnJhbWVgIGlzIHNpbXBseSBkYXRhIGFycmFuZ2VkIGFzIGEgdGFibGUgb2Ygcm93cyBhbmQgY29sdW1ucy4KCmBgYHtyfQpwb3B1bGF0aW9uCmBgYAoKV2UgZGVmaW5lIGEgdmFyaWFibGUgY2FsbGVkIGBzYW1wbGVfc2l6ZWAgYW5kIGdpdmUgaXQgYSB2YWx1ZSBvZiAxMDAuCgpgYGB7cn0Kc2FtcGxlX3NpemUgPC0gMTAwCmBgYAoKV2UgdGhlbiByYW5kb21seSBzYW1wbGUsIGluIHRoaXMgY2FzZSwgYHNhbXBsZV9zaXplYCBudW1iZXIgb2Ygcm93cyBmcm9tIHRoaXMgZGF0YSBmcmFtZToKCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKQpgYGAKCk5leHQgd2UgYG11dGF0ZWAgdGhlIGRhdGEgZnJhbWUgYnkgYWRkaW5nIGEgbmV3IGNvbHVtbiBjYWxsZWQgYHVuaXRfdHhfZWZmZWN0YCB3aGljaCBpcyBkZWZpbmVkIGFzIGBwb3RlbnRpYWxfMWAgLSBgcG90ZW50aWFsXzBgIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byBwb3RlbnRpYWwgb3V0Y29tZXMuICBUaGUgdmVyYiBgbXV0YXRlYCBzaW1wbHkgbWVhbnMgdGhhdCB3ZSdyZSBtb2RpZnlpbmcgb3IgYWRkaW5nIHRvIHRoZSBkYXRhIGZyYW1lIHdpdGggdGhpcyBvcGVyYXRpb24uCgpgYGB7cn0KcG9wdWxhdGlvbiAlPiUKICBzYW1wbGVfbihzYW1wbGVfc2l6ZSkgJT4lCiAgbXV0YXRlKHVuaXRfdHhfZWZmZWN0ID0gcG90ZW50aWFsXzEgLSBwb3RlbnRpYWxfMCkKYGBgCllvdSB3aWxsIG5vdGljZSB0aGF0IHRoaXMgbmV3IGNvbHVtbiBpcyBjcmVhdGVkIGFzIHRoZSBsYXN0IChyaWdodC1tb3N0KSBjb2x1bW4gb2YgdGhpcyBkYXRhIGZyYW1lLgoKTmV4dCwgd2UgYGFycmFuZ2VgIHRoZSByb3dzIG9mIHRoZSBkYXRhIGZyYW1lIGJ5IGBydW5pZihuKCkpYC4KCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpCmBgYAogVGhlIGBydW5pZihuKCkpYCBmdW5jdGlvbiBzaW1wbHkgZ2VuZXJhdGVzIHJhbmRvbSBjaG9zZW4gbnVtYmVycyBiZXR3ZWVuIDAgYW5kIDEgZm9yIGVhY2ggcm93IGluIHRoZSBkYXRhIGZyYW1lLiBgYXJyYW5nZWAgc29ydHMgdGhlIGRhdGEgZnJhbWUgYnkgdGhpcyByb3cgYW5kIHdlIGhhdmUgcm93cyB0aGF0IGFyZSBub3cgaW4gcmFuZG9tIG9yZGVyLgogCiBZb3UgY2FuIHBsYXkgd2l0aCBgcnVuaWYoKWAgYmVsb3cgdG8gc2VlIGhvdyBpdCB3b3JrczoKCmBgYHtyfQpydW5pZigxMCkKYGBgCgpXaHkgZGlkIHdlIG9yZGVyIHRoZSByb3dzIHJhbmRvbWx5PyBXZWxsIHRoaXMgbGVhZHMgdG8gb3VyIG5leHQgc3RlcCBpbiBgbXV0YXRlYC1pbmcgdGhlIGZpcnN0IGhhbGYgb2YgdGhlIHJvd3MgYXMgdHJlYXRtZW50LCBhbmQgdGhlIHNlY29uZCBoYWxmIG9mIHRoZSByb3dzIGFzIGNvbnRyb2wuIFNpbmNlIHRoZXkgYXJlIHJhbmRvbWx5IG9yZGVyZWQsIHRoaXMgd2lsbCBjcmVhdGUgb3VyIHJhbmRvbSBhc3NpZ25tZW50IG9mIHRyZWF0bWVudC4KCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogIG11dGF0ZShncm91cCA9IGlmX2Vsc2Uocm93X251bWJlcigpIDw9IG4oKSAvIDIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKQpgYGAKClRoZSBgaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIilgIGV4cHJlc3Npb24gdGVsbHMgdXMgdGhhdCBpZiB0aGUgcm93IG51bWJlciBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHRvdGFsIHJvdyBudW1iZXJzIGRpdmlkZWQgYnkgMiwgYXNzaWduIHRoZSB2YWx1ZSBgdHJlYXRtZW50YCBvdGhlcndpc2UgYXNzaWduIHRoZSB2YWx1ZSBgY29udHJvbGAuIFlvdSB3aWxsIG5vdGljZSBhIG5ldyBjb2x1bW4gY2FsbGVkIGBncm91cGAgY3JlYXRlZCBvbiB0aGUgcmlnaHRtb3N0IHNpZGUgb2YgdGhpcyBkYXRhIGZyYW1lLgoKVGhlIG5leHQgYGFycmFuZ2VgIHNpbXBseSBvcmRlcnMgdGhlIHJvd3MgaW4gdGhlIG9yaWdpbmFsIGBpZGAgb3JkZXIsIHRoZXJlJ3Mgbm8gc3BlY2lhbCByZWFzb24gdG8gZG8gdGhpcyBleGNlcHQga2VlcGluZyBvdXIgZGF0YSBmcmFtZSBpbiBvcmRlciBvZiBpZCBudW1iZXJzLiBUaGUgbmV4dCBzdGVwIGRlZmluZXMgc29tZSBtb3JlIGNvbHVtbnMgaW4gb3VyIGRhdGEgZnJhbWUuIAoKYGBge3J9CnBvcHVsYXRpb24gJT4lCiAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogIG11dGF0ZSh1bml0X3R4X2VmZmVjdCA9IHBvdGVudGlhbF8xIC0gcG90ZW50aWFsXzApICU+JQogIGFycmFuZ2UocnVuaWYobigpKSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogIGFycmFuZ2UoaWQpICU+JQogIG11dGF0ZShvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSkKYGBgCgpTaW1pbGFyIHRvIHRoZSBgdHJlYXRtZW50YCB2cyBgY29udHJvbGAgYXNzaWdubWVudCB0byB0aGUgYGdyb3VwYCBjb2x1bW4sIHRoZXNlIHR3byBuZXcgY29sdW1ucyBkZWZpbmUgdGhlIG9ic2VydmVkIG91dGNvbWUgaW4gdGhlIGB0cmVhdG1lbnRgIGdyb3VwLCBgb2JzZXJ2ZWRfMWAgYW5kIHRoZSBvYnNlcnZlZCBvdXRjb21lIGluIHRoZSBjb250cm9sIGdyb3VwLCBgb2JzZXJ2ZWRfMGAuIFRoZSBsb2dpYyBzaG91bGQgbG9vayBmYW1pbGlhcjoKCmBvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLCBOQV9yZWFsXylgIHN1Z2dlc3RzIHRoYXQgaWYgdGhlIGdyb3VwIGNvbHVtbiBoYXMgdGhlIHZhbHVlIGBjb250cm9sYCB0aGVuIHdlIHNob3VsZCBhc3NpZ24gYG9ic2VydmVkXzBgIHRoZSB2YWx1ZSBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWUgd2l0aG91dCB0cmVhdG1lbnQsIGBwb3RlbnRpYWxfMGAuIE90aGVyd2lzZSwgdGhlIGNvbHVtbiBnZXRzIGFuIGBOQV9yZWFsX2AgdmFsdWUgd2hpY2ggc2ltcGx5IG1lYW5zIHRoZSBkYXRhIGlzIG1pc3Npbmcgc2luY2UgdGhlIGluZGl2aWR1YWwgd2FzIGluIHRoZSBvdGhlciBgdHJlYXRtZW50YCBncm91cC4gVGhlIHNhbWUgYXBwbGllcyB0byB0aGUgY3JlYXRpb24gb2YgdGhlIGBvYnNlcnZlZF8xYCBjb2x1bW4uCgpUaGUgbmV4dCBjaHVuayBvZiBjb2RlIGJlZ2lubmluZyB3aXRoIGBiaW5kX3Jvd3NgIGlzIGZhaXJseSBjb21wbGljYXRlZCBhbmQgbm90IHBhcnRpY3VsYXJseSBlbGVnYW50LiBJIHdpbGwgc2tpcCB0aGUgZGV0YWlsZWQgZXhwbGFuYXRpb24gZm9yIG5vdyBidXQgdGhlIHB1cnBvc2Ugb2YgdGhpcyBzdGVwIGlzIHRvIGNyZWF0ZSBhIHN1bW1hcnkgcm93IHRoYXQgdGFrZXMgdGhlIG1lYW4gb2YgYWxsIHRoZSBudW1lcmljIGNvbHVtbnMgb2YgdGhlIGRhdGEgYW5kIHJvdW5kcyB0aGVtIHRvIHRoZSBmaXJzdCBzaWduaWZpY2FudCBkaWdpdC4gSXQgYWxzbyByZXBsYWNlcyBjb2x1bW5zIHRoYXQgYXJlIG9mIGEgbm9uLW51bWVyaWMgY2hhcmFjdGVyIHR5cGUgYW5kIHJlcGxhY2VzIGl0IHdpbGwgdGhlIHZhbHVlIGA8PFNVTU1BUlk+PmAuIAoKYGBge3J9CnBvcHVsYXRpb24gJT4lCiAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogIG11dGF0ZSh1bml0X3R4X2VmZmVjdCA9IHBvdGVudGlhbF8xIC0gcG90ZW50aWFsXzApICU+JQogIGFycmFuZ2UocnVuaWYobigpKSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogIGFycmFuZ2UoaWQpICU+JQogIG11dGF0ZShvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSkgJT4lCiAgYmluZF9yb3dzKAogICAgc3VtbWFyaXNlKC4sIGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgICAgIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCB+ICI8PFNVTU1BUlk+PiIpLAogICAgICAgICAgICAgaWQgPSBOQSkpCmBgYAoKTGV0J3MgZG8gYSBzbGlnaHQgbW9kaWZpY2F0aW9uIG9mIHRoZSBjb2RlIGJ5IHRha2luZyBvdXQgdGhlIGBiaW5kX3Jvd3NgIGZ1bmN0aW9uIHRvIGdldCBhbiBpbnR1aXRpb24gb2Ygd2hhdCBtaWdodCBiZSBnb2luZyBvbiBoZXJlOgoKYGBge3J9CnBvcHVsYXRpb24gJT4lCiAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogIG11dGF0ZSh1bml0X3R4X2VmZmVjdCA9IHBvdGVudGlhbF8xIC0gcG90ZW50aWFsXzApICU+JQogIGFycmFuZ2UocnVuaWYobigpKSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogIGFycmFuZ2UoaWQpICU+JQogIG11dGF0ZShvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSkgJT4lCiAgc3VtbWFyaXNlKC4sIGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgcm91bmQsIGRpZ2l0cyA9IDEpLAogICAgICAgICBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiAiPDxTVU1NQVJZPj4iKSwKICAgICAgICAgaWQgPSBOQSkKYGBgCldlIHNlZSB0aGF0IHdlIGNyZWF0ZSBhIHNpbmdsZSByb3cgdGhhdCBhdmVyYWdlcyB0aGUgdmFsdWVzIGZyb20gZWFjaCBudW1lcmljIGNvbHVtbiAoaWdub3JpbmcgdGhlIG1pc3NpbmcgTkEgdmFsdWVzKSBhbmQgcm91ZG5zIGl0IHRvIGEgc2luZ2xlIHNpZ25pZmljYW50IGRpZ2l0LiBUaGlzIHJvdyBpcyBzaW1wbHkgYWRkZWQgYXMgdGhlIGZpcnN0IHJvdyBpbiB0aGUgZGF0YSBmcmFtZSBhbmQgdGhlbiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGBvYnNlcnZlZF8xYCBhbmQgYG9ic2VydmVkXzBgIGlzIHRha2VuIGFzIHRoZSBgb2JzZXJ2ZWRfYXRlYDoKCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogIG11dGF0ZShncm91cCA9IGlmX2Vsc2Uocm93X251bWJlcigpIDw9IG4oKSAvIDIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKSAlPiUKICBhcnJhbmdlKGlkKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXyksCiAgICAgICAgIG9ic2VydmVkXzEgPSBpZl9lbHNlKGdyb3VwID09ICJ0cmVhdG1lbnQiLCBwb3RlbnRpYWxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogIGJpbmRfcm93cygKICAgIHN1bW1hcmlzZSguLCBhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIG1lYW4sIG5hLnJtID0gVFJVRSkpICU+JQogICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksCiAgICAgICAgICAgICBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiAiPDxTVU1NQVJZPj4iKSwKICAgICAgICAgICAgIGlkID0gTkEpKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfYXRlID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogIGFycmFuZ2UoZGVzYyhyb3dfbnVtYmVyKCkpKSAlPiUKICBzZWxlY3QoLWlkKQpgYGAKQXMgYSBmaW5pc2hpbmcgdG91Y2ggd2UgcmV2ZXJzZSB0aGUgb3JkZXIgb2YgdGhlIHJvd3Mgc28gdGhhdCB0aGUgbmV3bHkgYWRkZWQgc3VtbWFyeSByb3cgaXMgb24gdG9wLCBhbmQgdGhlbiByZW1vdmUgdGhlIGBpZGAgY29sdW1uIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgb2YgdGhlIGRhdGEgZnJhbWUgZGlzcGxheWVkLg==